﻿using CK.Sprite.ThirdContract;
using Dapper;
using Dapper.Contrib.Extensions;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace CK.Sprite.Framework
{
    public class RepositoryBase<T,TKeyType> : IRepository<T, TKeyType> where T : class
    {
        private ICurrentUser _currentUser;
        public RepositoryBase(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _currentUser = ServiceLocator.ServiceProvider.GetService<ICurrentUser>();
        }

        public IUnitOfWork _unitOfWork { get; private set; }

        public bool Delete(T obj)
        {
            if(ApplyDeletionInfo(obj))
            {
                return Update(obj);
            }
            else
            {
                return _unitOfWork.Connection.Delete(obj, _unitOfWork.Transaction);
            }
        }

        public bool Delete(IEnumerable<T> list)
        {
            if (ApplyDeletionInfos(list))
            {
                return Update(list);
            }
            else
            {
                return _unitOfWork.Connection.Delete(list, _unitOfWork.Transaction);
            }
        }

        public bool DeleteAll()
        {
            if(typeof(T) is IDeletionAudited)
            {
                var strUpdateSql = $"UPDATE {TableNameMapper(typeof(T))} SET IsDeleted=1 AND DeletionTime=@DeletionTime AND DeleterId=@DeleterId WHERE IsDeleted=0;";
                return _unitOfWork.Connection.ExecuteScalar<bool>(strUpdateSql, new { DeletionTime=DateTime.Now, DeleterId = _currentUser.UserId }, transaction: _unitOfWork.Transaction);
            }
            else
            {
                return _unitOfWork.Connection.DeleteAll<T>(_unitOfWork.Transaction);
            }
        }

        public T Get(TKeyType id)
        {
            if (typeof(T) is IDeletionAudited)
            {
                var strGetSql = $"SELECT * FROM {TableNameMapper(typeof(T))} WHERE Id=@Id AND IsDeleted=0;";
                return _unitOfWork.Connection.QueryFirstOrDefault<T>(strGetSql, new { Id = id }, transaction: _unitOfWork.Transaction);
            }
            else
            {
                return _unitOfWork.Connection.Get<T>(id, _unitOfWork.Transaction);
            }
        }

        public IEnumerable<T> GetAll()
        {
            if (typeof(T) is IDeletionAudited)
            {
                var strGetSql = $"SELECT * FROM {TableNameMapper(typeof(T))} WHERE IsDeleted=0;";
                return _unitOfWork.Connection.Query<T>(strGetSql, transaction: _unitOfWork.Transaction);
            }
            else
            {
                return _unitOfWork.Connection.GetAll<T>(_unitOfWork.Transaction);
            }            
        }

        public long Insert(T obj)
        {
            ApplyCreationInfo(obj);
            return _unitOfWork.Connection.Insert<T>(obj, _unitOfWork.Transaction);
        }

        public long Insert(IEnumerable<T> list)
        {
            ApplyCreationInfos(list);
            return _unitOfWork.Connection.Insert(list, _unitOfWork.Transaction);
        }

        public bool Update(T obj)
        {
            ApplyModificationInfo(obj);
            return _unitOfWork.Connection.Update(obj, _unitOfWork.Transaction);
        }

        public bool Update(IEnumerable<T> list)
        {
            ApplyModificationInfos(list);
            return _unitOfWork.Connection.Update(list, _unitOfWork.Transaction);
        }

        public async Task<bool> DeleteAsync(T obj)
        {
            if (ApplyDeletionInfo(obj))
            {
                return await UpdateAsync(obj);
            }
            else
            {
                return await _unitOfWork.Connection.DeleteAsync(obj, _unitOfWork.Transaction);
            }
        }

        public async Task<bool> DeleteAsync(IEnumerable<T> list)
        {
            if (ApplyDeletionInfos(list))
            {
                return await UpdateAsync(list);
            }
            else
            {
                return await _unitOfWork.Connection.DeleteAsync(list, _unitOfWork.Transaction);
            }
        }

        public async Task<bool> DeleteAllAsync()
        {
            if (typeof(T) is IDeletionAudited)
            {
                var strUpdateSql = $"UPDATE {TableNameMapper(typeof(T))} SET IsDeleted=1 AND DeletionTime=@DeletionTime AND DeleterId=@DeleterId WHERE IsDeleted=0;";
                return await _unitOfWork.Connection.ExecuteScalarAsync<bool>(strUpdateSql, new { DeletionTime = DateTime.Now, DeleterId = _currentUser.UserId }, transaction: _unitOfWork.Transaction);
            }
            else
            {
                return await _unitOfWork.Connection.DeleteAllAsync<T>(_unitOfWork.Transaction);
            }
            
        }

        public async Task<T> GetAsync(TKeyType id)
        {
            if (typeof(T) is IDeletionAudited)
            {
                var strGetSql = $"SELECT * FROM {TableNameMapper(typeof(T))} WHERE Id=@Id AND IsDeleted=0;";
                return await _unitOfWork.Connection.QueryFirstOrDefaultAsync<T>(strGetSql, new { Id = id }, transaction: _unitOfWork.Transaction);
            }
            else
            {
                return await _unitOfWork.Connection.GetAsync<T>(id, _unitOfWork.Transaction);
            }
        }

        public async Task<IEnumerable<T>> GetAllAsync()
        {
            if (typeof(T) is IDeletionAudited)
            {
                var strGetSql = $"SELECT * FROM {TableNameMapper(typeof(T))} WHERE IsDeleted=0;";
                return await _unitOfWork.Connection.QueryAsync<T>(strGetSql, transaction: _unitOfWork.Transaction);
            }
            else
            {
                return await _unitOfWork.Connection.GetAllAsync<T>(_unitOfWork.Transaction);
            }
        }

        public async Task<long> InsertAsync(T obj)
        {
            ApplyCreationInfo(obj);
            return await _unitOfWork.Connection.InsertAsync(obj, _unitOfWork.Transaction);
        }

        public async Task<long> InsertAsync(IEnumerable<T> list)
        {
            ApplyCreationInfos(list);
            return await _unitOfWork.Connection.InsertAsync(list, _unitOfWork.Transaction);
        }

        public async Task<bool> UpdateAsync(T obj)
        {
            ApplyModificationInfo(obj);
            return await _unitOfWork.Connection.UpdateAsync(obj, _unitOfWork.Transaction);
        }

        public async Task<bool> UpdateAsync(IEnumerable<T> list)
        {
            ApplyModificationInfos(list);
            return await _unitOfWork.Connection.UpdateAsync(list, _unitOfWork.Transaction);
        }

        private string TableNameMapper(Type type)
        {
            dynamic tableattr = type.GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == "TableAttribute");
            var name = type.Name;

            if (tableattr != null)
            {
                name = tableattr.Name;
            }

            return name;
        }

        #region Audited

        private void ApplyCreationInfos(IEnumerable<T> objs)
        {
            if(objs == null || objs.Count() == 0)
            {
                return;
            }
            if (!(objs.First() is ICreationAudited))
            {
                return;
            }

            foreach(var obj in objs)
            {
                ApplyCreationInfo(obj);
            }
        }
        private void ApplyCreationInfo(T obj)
        {
            if (!(obj is ICreationAudited objCreationAudited))
            {
                return;
            }
            objCreationAudited.CreatorId = _currentUser.UserId;
            objCreationAudited.CreationTime = DateTime.Now;
        }

        private void ApplyModificationInfos(IEnumerable<T> objs)
        {
            if (objs == null || objs.Count() == 0)
            {
                return;
            }
            if (!(objs.First() is IModificationAudited))
            {
                return;
            }

            foreach (var obj in objs)
            {
                ApplyModificationInfo(obj);
            }
        }
        private void ApplyModificationInfo(T obj)
        {
            if (!(obj is IModificationAudited objModificationAudited))
            {
                return;
            }
            objModificationAudited.LastModifierId = _currentUser.UserId;
            objModificationAudited.LastModificationTime = DateTime.Now;
        }

        // 返回是否应用删除审计
        private bool ApplyDeletionInfos(IEnumerable<T> objs)
        {
            if (objs == null || objs.Count() == 0)
            {
                return false;
            }
            if (!(objs.First() is IDeletionAudited))
            {
                return false;
            }

            foreach (var obj in objs)
            {
                ApplyDeletionInfo(obj);
            }

            return true;
        }

        // 返回是否应用删除审计
        private bool ApplyDeletionInfo(T obj)
        {
            if (!(obj is IDeletionAudited objDeletionAudited))
            {
                return false;
            }
            objDeletionAudited.IsDeleted = true;
            objDeletionAudited.DeleterId = _currentUser.UserId;
            objDeletionAudited.DeletionTime = DateTime.Now;

            return true;
        }

        #endregion
    }

    public class GuidRepositoryBase<T> : RepositoryBase<T, Guid>, IGuidRepository<T> where T : class
    {
        public GuidRepositoryBase(IUnitOfWork unitOfWork) : base(unitOfWork)
        {

        }
    }
}
